using System;
using System.ComponentModel;
using System.Collections;
using Microsoft.Win32;
using System.IO;
using System.Diagnostics;
using System.Windows.Forms;
using System.Configuration.Install;

namespace MsPartners.Unisys.BizTalkAdapters.AdapterWizard
{
	/// <summary>
	/// Summary description for WizardInstaller.
	/// </summary>
	[RunInstaller(true)]
	public class WizardInstaller : System.Configuration.Install.Installer
	{
		private string vsDirPath = null;
		private System.ComponentModel.Container components = null;
		private bool generalError = false;
		private string bizTalkInstallPath = null;
		private string visualStudioInstallPath = null;
		private string dotNetFrameworkPath = null;
		private Exception exception = null;

		public WizardInstaller()
		{
			// This call is required by the Designer.
			InitializeComponent();

			const string bizTalkKey = @"SOFTWARE\Microsoft\BizTalk Server\3.0";
			const string visualStudioKey = @"SOFTWARE\Microsoft\VisualStudio\8.0";
			const string dotNetFrameworkKey = @"SOFTWARE\Microsoft\.NETFramework";
			try
			{
				RegistryKey bizTalkReg = Registry.LocalMachine.OpenSubKey(bizTalkKey);
				bizTalkInstallPath = bizTalkReg.GetValue("InstallPath").ToString();
				bizTalkReg.Close();
				vsDirPath = bizTalkInstallPath + 
					@"Developer Tools\BizTalkProjects\BTSProjects.vsdir";

				RegistryKey visualStudioReg = Registry.LocalMachine.OpenSubKey(visualStudioKey);
				visualStudioInstallPath = visualStudioReg.GetValue("InstallDir").ToString();
				visualStudioReg.Close();

				RegistryKey DotNetFramework = Registry.LocalMachine.OpenSubKey(dotNetFrameworkKey);
				dotNetFrameworkPath = DotNetFramework.GetValue("InstallRoot").ToString();
				DotNetFramework.Close();
			}
			catch(Exception e)
			{
				Context.LogMessage(e.Message);
				exception = e;
				generalError = true;
			}
		}

		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		private void InitializeComponent()
		{
			components = new System.ComponentModel.Container();
		}

		public override void Commit(System.Collections.IDictionary savedState)
		{
			base.Commit(savedState);
			try
			{
				if (generalError)
					throw exception;
			}
			catch(Exception e)
			{
				Context.LogMessage(e.Message);
				throw e;
			}
		}

		public override void Install(System.Collections.IDictionary stateSaver)
		{
			base.Install(stateSaver);
			try
			{
				if (generalError)
					throw exception;
				
				while (!AddVszFile());
				while (!AddVsDirLine());
				while (!CompileBaseClasses());
				while (!RegisterAdapterWizard(false));
			}
			catch(Exception e)
			{
				Context.LogMessage(e.Message);
				throw e;
			}
		}

		public override void Rollback(System.Collections.IDictionary savedState)
		{
			try
			{
				if (generalError)
					throw exception;
				RemoveVszFile();
			}
			catch(Exception e)
			{
				Context.LogMessage(e.Message);
			}
			base.Rollback(savedState);
		}

		public override void Uninstall(System.Collections.IDictionary savedState)
		{
			try
			{
				if (generalError)
					throw exception;
				RemoveVszFile();
				RegisterAdapterWizard(true);
			}
			catch(Exception e)
			{
				Context.LogMessage(e.Message);
			}
			base.Uninstall(savedState);
		}

		/// <summary>
		/// Adds a line to the vsdir file to know which icon to use, get a description etc.
		/// </summary>
		private bool AddVsDirLine()
		{					  
			Context.LogMessage(">> AddVsDirLine");
			try
			{
				string strNewLine = 
					"BizTalkAdapterWizard.vsz| |BizTalk Server Adapter Project|300|Creates a BizTalk Server adapter|{ef7e327e-cd33-11d4-8326-00c04fa0ce8d}|226| |#133";
				if ((File.GetAttributes(vsDirPath) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
				{
					File.SetAttributes(vsDirPath,FileAttributes.Normal);
				}
				StreamReader sr = new StreamReader(vsDirPath);
				string strVsDir = sr.ReadToEnd();
				sr.Close();

				if (strVsDir.IndexOf("BizTalkAdapterWizard.vsz") == -1)
				{
					StreamWriter sw = File.AppendText(vsDirPath);
					sw.WriteLine(strNewLine);
					sw.Close();
				}
				File.SetAttributes(vsDirPath,FileAttributes.ReadOnly);
			}
			catch(Exception e)
			{
				return HandleError("AddVsDirLine",e);
			}
			return true;
		}

		/// <summary>
		/// Adds a vszFile to the BT dir.
		/// </summary>
		private bool AddVszFile()
		{
			Context.LogMessage(">> AddVszFile");
			try
			{
				string vszFileName = bizTalkInstallPath + 
					@"Developer Tools\BizTalkProjects\BizTalkAdapterWizard.vsz";
				FileInfo fi = new FileInfo(vszFileName);
				if (fi.Exists)
				{
					fi.Delete();
				}
				using(TextWriter VszWriter = new StreamWriter(vszFileName,false))
				{
					VszWriter.WriteLine("VSWIZARD 7.0");
					VszWriter.WriteLine("Wizard=VSWizard.BizTalkAdapterWizard");
					VszWriter.WriteLine("Param=\"WIZARD_NAME = BizTalkAdapterWizard\"");
					VszWriter.WriteLine("Param=\"WIZARD_UI = FALSE\"");
					VszWriter.WriteLine("Param=\"PROJECT_TYPE = CSPROJ\"");
				}
			}
			catch(Exception e)
			{
				return HandleError("AddVszFile",e);
			}
			return true;
		}


		/// <summary>
		/// Removes a vszFile from the BT dir.
		/// </summary>
		private void RemoveVszFile()
		{
			Context.LogMessage(">> RemoveVszFile");
			try
			{
				string vszFileName = bizTalkInstallPath + 
					@"Developer Tools\BizTalkProjects\BizTalkAdapterWizard.vsz";
				FileInfo fi = new FileInfo(vszFileName);
				if (fi.Exists)
				{
					fi.Delete();
				}
			}
			catch(Exception e)
			{
				Context.LogMessage(e.Message);
			}
		}

		/// <summary>
		/// Builds the baseclasses strong name key
		/// </summary>
		private void CreateStrongNameForBaseClasses()
		{
			Context.LogMessage(">> CreateStrongNameForBaseClasses");
			try
			{
				string keyFileName = bizTalkInstallPath + 
					@"SDK\Samples\AdaptersDevelopment\BaseAdapter\v1.0.2\BaseAdapter.snk";

				FileInfo fi = new FileInfo(keyFileName);
				if (fi.Exists)
					return;

				string argument = " -k \"" + keyFileName + "\" ";
		
				string stringToRemove = @"Common7\IDE\";
				string command = visualStudioInstallPath.Remove(visualStudioInstallPath.IndexOf(stringToRemove),stringToRemove.Length);
				command += @"SDK\v2.0\Bin\sn.exe";

				Context.LogMessage("command = " + command + ", argument = " + argument);
				
				ProcessStartInfo psi = new ProcessStartInfo(command,argument);
				psi.WindowStyle = ProcessWindowStyle.Hidden;
				Process p = Process.Start(psi);
				p.WaitForExit(60*1000);
			}
			catch(Exception e)
			{
				Context.LogMessage("CreateStrongNameForBaseClasses error: " + e.Message);
			}
		}
		
		/// <summary>
		/// Builds the baseclasses dll
		/// </summary>
		private bool CompileBaseClasses()
		{
			Context.LogMessage(">> CompileBaseClasses");
			try
			{
				CreateStrongNameForBaseClasses();
				string projectName = bizTalkInstallPath +
					@"SDK\Samples\AdaptersDevelopment\BaseAdapter\v1.0.2\Microsoft.Samples.BizTalk.Adapter.Common.csproj";
				FileInfo fi = new FileInfo(projectName);
				if (!fi.Exists)
					throw new Exception("Unable to compile the base classes dll " +
						"(could not find \"" + projectName + "\"). Make sure you " + 
						"have installed the BizTalk Server 2006 SDK.");
				string argument = "\"" + projectName + "\" /Build release";
		
				string command = "\"" + visualStudioInstallPath + "DevEnv.exe\"";
				Context.LogMessage("command = " + command + ", argument = " + argument);
				Process p = Process.Start(command,argument);
				p.WaitForExit(60*1000);
			}
			catch(Exception e)
			{
				return HandleError("CompileBaseClasses",e);
			}
			return true;
		}

		/// <summary>
		/// Registers the adapter dll with codebase. The default
		/// installer doesn't do that.
		/// </summary>
		/// <returns></returns>
		private bool RegisterAdapterWizard(bool unregister)
		{
			Context.LogMessage(">> RegisterAdapterWizard");
			try
			{
				string targetDir = Context.Parameters["ApplicationPath"];
				if(!targetDir.EndsWith(@"\"))
				{
					targetDir += @"\";
				}
				string regCommand = dotNetFrameworkPath + @"v2.0.50727\RegAsm.exe";
					
				if (unregister)
					regCommand += " /u";
				string argument = @"""" + targetDir + 
					@"AdapterWizard.dll"" /codebase /s";

				Context.LogMessage("regCommand = " + regCommand + ", argument = " + argument);

				ProcessStartInfo psi = new ProcessStartInfo(regCommand,argument);
				psi.WindowStyle = ProcessWindowStyle.Hidden;
				Process p = Process.Start(psi);
				p.WaitForExit(60*1000);
			}
			catch(Exception e)
			{
				if (!unregister)
					return HandleError("RegisterAdapterWizard",e);
				else
					return true;//don't want to stop in uninstall.
			}
			return true;
		}

		/// <summary>
		/// Generic error handler. Logs and asks for action
		/// </summary>
		/// <param name="e"></param>
		/// <returns></returns>
		private bool HandleError(string functionName,Exception e)
		{
			Context.LogMessage(">> HandleError");
			Context.LogMessage(e.Message);

			DialogResult result = 
				MessageBox.Show(e.Message,
				"AdapterWizard Installer",
				MessageBoxButtons.AbortRetryIgnore,
				MessageBoxIcon.Error,
				MessageBoxDefaultButton.Button1);
			switch(result)
			{
				case DialogResult.Abort:
					throw e;
				case DialogResult.Retry:
					return false;
				case DialogResult.Ignore:
				{
					if (MessageBox.Show(
						"If you choose to ignore you will have to perform " +
						"some actions manually as described in the ReadMe. " +
						Environment.NewLine + 
						"Continue?",
						"AdapterWizard Installer - " + functionName,
						MessageBoxButtons.YesNo,
						MessageBoxIcon.Question) == DialogResult.Yes)
					{
						break;
					}
					else
					{
						throw e;
					}
				}
			}
			return true;
		}
	}
}
